package com.sogoodsoft.dao.common;

import java.io.File;
import java.io.Serializable;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Restrictions;
import org.springframework.util.Assert;
import org.springside.modules.orm.Page;
import org.springside.modules.orm.PropertyFilter;
import org.springside.modules.orm.PropertyFilter.MatchType;
import org.springside.modules.orm.hibernate.HibernateDao;

/**
 * 增加分页子查询
 * 不支持子查询中包含OR运算符
 * 
 * @author Administrator
 * @param <T>
 * @param <PK>
 */
public class CommonHibernateDao<T, PK extends Serializable> extends HibernateDao<T, PK> {

	
	private static Log log = LogFactory.getLog(CommonHibernateDao.class); 
	
	protected Criteria criteria;
	
	/**
	 * 预定义查询语句配置文件的完整路径 非"/"开始  eg:com/infosys/dao/project/myHQL.xml 
	 * 默认值为当前dao下的query.xml文件
	 */
	protected String queryCfg="";
	
	public  CommonHibernateDao(){
		super();
	}	
	
	@Override
	public Page<T> findPage(final Page<T> page, final List<PropertyFilter> filters) {
		Criterion[] criterions = buildCriterionByPropertyFilter(filters);
		return findPage(page, criterions);
	}
		
	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Page<T> findPage(final Page<T> page, final Criterion... criterions) {
		Assert.notNull(page, "page涓嶈兘涓虹┖");

		Criteria c = createCriteria(criterions);

		if (page.isAutoCount()) {
			long totalCount = countCriteriaResult(c);
			page.setTotalCount(totalCount);
		}

		setPageParameterToCriteria(c, page);

		List result = c.list();
		page.setResult(result);
		return page;
	}
	
	public Criteria createCriteria(final Criterion... criterions) {
		if(criteria==null){
			//普通查询，没有二级属性，需直接创建查询
		criteria = getSession().createCriteria(entityClass);
			//criteria = this.sessionFactory.openSession().createCriteria(entityClass);
		}
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}
	
	@Override
	protected Criterion[] buildCriterionByPropertyFilter(final List<PropertyFilter> filters) {
		criteria = getSession().createCriteria(entityClass);
		List<Criterion> criterionList = new ArrayList<Criterion>();
		for (PropertyFilter filter : filters) {
			if (!filter.hasMultiProperties()) { //鍙湁涓�釜灞炴�闇�姣旇緝鐨勬儏鍐�
				if(filter.getPropertyName().indexOf("_")>0){
					//带有子查询
					Criterion criterion =subQuery(filter.getPropertyName(), filter.getMatchValue(), filter
							.getMatchType());
					criterionList.add(criterion);
				}else{
					//没有子查询的简单查询
					Criterion criterion = buildCriterion(filter.getPropertyName(), filter.getMatchValue(), filter
							.getMatchType());
					criterionList.add(criterion);
				}
				
			} else {//鍖呭惈澶氫釜灞炴�闇�姣旇緝鐨勬儏鍐�杩涜or澶勭悊.
				Disjunction disjunction = Restrictions.disjunction();
				for (String param : filter.getPropertyNames()) {
					Criterion criterion = buildCriterion(param, filter.getMatchValue(), filter.getMatchType());
					disjunction.add(criterion);
				}
				criterionList.add(disjunction);
			}
		}
		return criterionList.toArray(new Criterion[criterionList.size()]);
	}
	

	protected Criterion subQuery(String propertyName,Object value, MatchType tp){
		String subProperName = getSubProperName(propertyName);
		String queryProperName = getQueryProperName(propertyName);
		this.criteria.createAlias(subProperName, propertyName);
		return buildCriterion(queryProperName,value,tp);
	}
	
	protected String getSubProperName(String propertyName){
		String subProperName = "";
		subProperName = propertyName;
		subProperName = StringUtils.substringBeforeLast(subProperName, "_");
		subProperName = subProperName.replaceAll("_", ".");
		logger.debug("subProperName="+subProperName);		
		return subProperName ;
	}
	protected String getQueryProperName(String propertyName){
		String queryProperName = propertyName;
		queryProperName = StringUtils.substringAfterLast(propertyName, "_");
		queryProperName = propertyName+"."+queryProperName;
		logger.debug("queryProperName="+queryProperName);	
		return queryProperName;
	}
	
	/**
	 * 命名查询
	 * @param key 查询语句名
	 * @param requestMap 字符串类型的参数，键值对
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List keyQuery(String key,Map<String, Object> requestMap){
		Query query=null;
		Map map = buildParaMap(key, requestMap);
		
		query = this.getSession().createQuery((String)map.get("__HQL"));
		map.remove("__HQL");
		Iterator<String> iterator = map.keySet().iterator();
		String name;
		while(iterator.hasNext()){
			name = iterator.next();
			query.setParameter(name, map.get(name));
		}
		return query.list();
	}
	
	
	/**
	 * 命名查询，支持分页
	 * @param key 查询语句名
	 * @param page 存储分页值的分页对象
	 * @param requestMap 字符串类型的参数，键值对
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Page keyQuery(String key,Page page,Map<String, Object> requestMap){

		Query query=null;
		Map map = buildParaMap(key, requestMap);
		
		//查询记录数
		long count = countRecord(key,map);
		
		//计算分页
		page.setTotalCount(count);
		int pageSize = page.getPageSize();
		long pageNo = page.getPageNo();		
		long totalPage =page.getTotalPages();
		if(pageNo>totalPage){
			pageNo = totalPage;
		}
		query = this.getSession().createQuery((String)map.get("__HQL"));
		map.remove("__HQL");
		log.debug("查询HQL："+query.getQueryString());
		Iterator<String> iterator = map.keySet().iterator();
		String name;
		while(iterator.hasNext()){
			name = iterator.next();
			query.setParameter(name, map.get(name));
			log.debug("命名查询:"+name+"="+map.get(name));
				

		}
		query.setMaxResults(pageSize);
		query.setFirstResult((int)(pageNo*pageSize-pageSize));
		page.setResult(query.list());
		return page;
	}
	

	
	/**
	 * 构造本次查询的参数和值，配合HQL配置文件，设置参数的默认值及参数的类型转换
	 * <p>额外返回值：__HQL 本次查询的HQL语句 两个下划线起始</p>
	 * @param key
	 * @param requestMap
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Map buildParaMap(String key,Map<String, Object> requestMap){
		Map<String, Object> paraMap = new HashMap<String, Object>();
		
		SAXReader reader = new SAXReader(); 
		String name,type,value,init,HQL;
		Object obj;
		String hqlKey = "__HQL";
		boolean need = false;
		try{
		Document doc = reader.read(new File(getConfigPath()));
		Element queryE = (Element)doc.selectSingleNode("//query[@name='"+key+"']");
		if(queryE!=null){

			//读取HQL语句
			HQL = queryE.getStringValue().trim();
			paraMap.put(hqlKey, HQL);
			
			//设置默认值
			List<Element> param = queryE.elements("query-param");
			for(Element e:param){
				name = e.attributeValue("name");
				type = e.attributeValue("type");
				init = e.attributeValue("init");
				obj = requestMap.get(name);
				
				
				
				value=null;
				if(obj!=null){
					if(obj.getClass().isArray()){						
						value = ((String[])obj)[0];	
					}else{
						value = String.valueOf(obj);
					}
					
					value = value.trim();
				}
				
				//处理符加条件，判断本参数是否必要
				need = dealNeed(name,value,paraMap);				
				if(!need){
					continue;
				}
				
				log.debug("当前参数"+name+"="+value);
				
				//字符串类型
				if(type==null||"String".equals(type)){
					if(value==null){
						value ="";						
					}
					if(init!=null){
					value = init.replace(name, value);
					}
					paraMap.put(name, value);				
				}else{
					//其它类型					
					if(value!=null){
						obj = CastToTypedValue(value,type);				
					}else{
					 obj = CastToTypedValue(init,type);
					}
					paraMap.put(name, obj);
				}
				
			}
		}
		}catch(Exception e){
			e.printStackTrace();
			log.error(e);
		}
		return paraMap;
	}
	

	
	/**
	 * 处理符加条件，处理后修改原有的HQL语句
	 * @param name
	 * @param obj
	 * @param paraMap
	 * @return
	 * 如果需要查需询正常where条件和附加条件，返回true，
	 * 如果不需要查询返回FALSE
	 */
	public boolean dealNeed(String name, String value,
			Map<String, Object> paraMap) {
		boolean need = true;
		String HQL = (String)paraMap.get("__HQL");		
		String additional  = findAdditional(HQL, name);

		//找到符加条件[ and username=:uname]
		if(additional!=null){
			
			//符加条件变量uname未传值，需要去除附加条件，不查询本变量
			if(value==null||value.length()==0){
				
				need = false;
				
				//修改保存HQL,去除本条件[ and username=:uname]
				HQL = HQL.replace(additional, "");	
				
			}else{//附加条件传值uname，需查询，修改[ and username=:uname]去掉[] 为  and username=:uname
				
				String newHQL = additional.replaceAll("[\\[|\\]]", "");
				
				HQL = HQL.replace(additional, newHQL);
			}
			paraMap.put("__HQL", HQL);
		}
		return need;
	}
	
	/**
	 * 查找符加条件
	 * @param HQL
	 * @param name
	 * @return 
	 * 查找到的符加条件语句  如 [ and username=:uname]
	 */
	public String findAdditional(String HQL,String name){
		String additional = null;
		Pattern pattern  =  Pattern.compile("(\\[[^\\[]+"+name+"[^\\]]*\\])");
		Matcher matcher = pattern.matcher(HQL);		
		while(matcher.find()){
			additional = matcher.group();
			break;
		}
		return additional;
	}

	public long countRecord(String key,Map<String, Object> paraMap){
		long count = 0;
		Query query=null;
		Map<String, Object> map = new HashMap<String, Object>(paraMap);
		String HQL = (String)map.get("__HQL");
		HQL = HQL.substring(HQL.indexOf("from"));
		HQL = "select count(*) " + HQL;

		log.debug("count HQL:");
		log.debug(HQL);

		query = this.getSession().createQuery(HQL);
		map.remove("__HQL");
		Iterator<String> iterator = map.keySet().iterator();
		String name;
		while(iterator.hasNext()){
			name = iterator.next();
			query.setParameter(name, map.get(name));
		}
		count = ((Long)query.uniqueResult()).longValue();
		
		return count;
	}
	
	/**
	 * <div>类型转换</div>
	 * <div>将String型的value转换为type指定的类型</div>
	 * <div>type定义：string、date[yyyy-MM-dd]、long、float、int</div>
	 * @param value
	 * @param type
	 * @return
	 */
	public Object CastToTypedValue(String value,String type){
		Object obj = null;
		try {
			if("String".equals(type)||type==null){
				obj = value;
			}else if("date".equals(type)){
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
				if(value==null){
					obj = new Date();
				}else{
					if(value.startsWith("month")){
						Calendar cal = Calendar.getInstance();
						cal.add(Calendar.MONTH,Integer.parseInt(value.replace("month", "")));
						obj = cal.getTime();
					}else{
					obj = sdf.parse(value);
					}
				}
					
				
			}else if("long".equals(type)){				
				if(value==null||value.length()==0){
					//obj=0;
					obj = new Long(0);
				}else{
					//obj = Long.parseLong(value);
					obj = new Long(value);
				}
			}else if("float".equals(type)){
				if(value==null||value.length()==0){
					//obj = 0;
					obj=new Float(value);
				}else{
					//obj = Float.parseFloat(value);
					obj = new Float(value);
				}
			}else{
				if(value==null||value.length()==0){
					obj=new Integer(value);
				}else{
					//obj = Integer.parseInt(value);
					obj = new Integer(value);
				}
			}
		} catch (NumberFormatException e) {
			e.printStackTrace();
			log.equals(e);
		} catch (ParseException e) {
			e.printStackTrace();
			log.equals(e);
		}
		return obj;
	}
	
	/**
	 * 获得配置文件的路径
	 * @return
	 */
	public String getConfigPath(){
		String path="";
		if(queryCfg.length()==0){
			String pkg = this.getClass().getPackage().getName();
			pkg = pkg.replace(".", "/");
			path = this.getClass().getResource("/").getPath()+pkg+"/query.xml";
		}else{
			path = this.getClass().getResource("/").getPath()+queryCfg;
		}
		return path;
	}
	
	public long getSequenceVal(String seq){
		SQLQuery  query = this.getSession().createSQLQuery("select "+seq+".nextval from dual");
		Object obj = query.uniqueResult();
		return ((BigDecimal)obj).longValue();
	}
}
